package com.rongx.config;

import com.rongx.cache.Cache;
import com.rongx.cache.impl.PerpetualCache;
import com.rongx.mapping.MappedStatement;
import com.rongx.transaction.TransactionFactory;
import com.rongx.transaction.jdbc.JdbcTransactionFactory;
import com.rongx.type.TypeAliasRegistry;
import com.rongx.type.TypeHandlerRegistry;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 全局配置核心类
 * 该类是类似MyBatis框架的配置中枢，负责整合框架运行所需的所有核心配置项
 * 包括数据源、事务工厂、SQL映射语句、缓存、类型别名、类型处理器等
 * 框架中所有核心组件的配置都通过此类统一管理和提供，是组件间数据交互的配置桥梁
 */
public class Configuration {

    /**
     * 环境配置对象
     * 通常包含数据源、事务管理器等与运行环境相关的基础配置
     * 可通过环境区分开发、测试、生产等不同运行场景的配置
     */
    protected Environment environment;

    /**
     * 存储SQL映射语句的集合
     * key为MappedStatement的唯一标识（一般是namespace+statementId，如"UserMapper.selectById"）
     * value为封装了SQL语句、参数类型、返回类型等信息的MappedStatement对象
     * 采用内部静态类StrictMap，确保同一ID的映射语句不会重复添加，避免冲突
     */
    protected final Map<String, MappedStatement> mappedStatements = new StrictMap<>("Mapped Statements collection");

    /**
     * 类型别名注册中心
     * 用于管理Java类型与别名的映射关系（如将"String"映射为java.lang.String）
     * 简化配置文件中类型的书写，避免冗长的全类名
     */
    protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();

    /**
     * 类型处理器注册中心
     * 管理JDBC类型与Java类型之间的转换逻辑
     * 例如将数据库VARCHAR类型转换为Java String类型，或反之，支撑参数绑定和结果集映射
     */
    protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();

    /**
     * 缓存容器
     * 存储框架中的各类缓存实例，key为缓存的唯一标识（一般是Mapper接口全类名）
     * 采用ConcurrentHashMap保证高并发场景下的线程安全，默认使用PerpetualCache实现
     */
    protected final Map<String, Cache> caches = new ConcurrentHashMap<>();

    /**
     * 全局缓存开关
     * 默认为true，开启后框架会对查询结果进行缓存，提升重复查询效率
     * 关闭后所有缓存相关功能失效，每次查询均直接操作数据库
     */
    protected boolean cacheEnabled = true;

    /**
     * 主键自动生成开关
     * 默认为false，开启后支持数据库自增主键的获取和绑定
     * 适用于插入数据时，需要获取数据库自动生成的主键值的场景
     */
    protected boolean useGeneratedKeys = false;

    /**
     * 默认SQL执行超时时间（单位：秒）
     * 未指定时为null，即使用数据库驱动的默认超时配置
     * 用于控制SQL语句执行的最长等待时间，避免长时间阻塞
     */
    protected Integer defaultStatementTimeout;

    /**
     * 默认结果集抓取大小
     * 未指定时为null，即使用数据库驱动的默认配置
     * 用于控制每次从数据库读取的结果集行数，优化大数据量查询的性能
     */
    protected Integer defaultFetchSize;

    /**
     * 日志实现类
     * 用于指定框架使用的日志组件（如SLF4J、Log4j等）
     * 统一框架的日志输出方式，便于问题排查和运行状态监控
     */
    protected LogImpl logImpl;

    /**
     * 事务工厂实例
     * 默认为JdbcTransactionFactory，负责创建JDBC类型的事务对象
     * 事务工厂封装了事务的创建逻辑，支持通过配置替换为其他事务实现（如分布式事务）
     */
    protected TransactionFactory transactionFactory = new JdbcTransactionFactory();

    /**
     * 数据源
     * 提供数据库连接的核心组件，框架通过数据源获取数据库连接
     * 可配置为C3P0、Druid等第三方数据源，支持连接池功能
     */
    protected DataSource dataSource;

    /**
     * 无参构造方法
     * 初始化配置类时，自动注册默认的类型别名和类型处理器
     * 确保框架基础的类型转换功能开箱即用
     */
    public Configuration() {
        // 注册框架预定义的默认类型别名（如基础数据类型、集合类型等）
        typeAliasRegistry.registerDefaultAliases();
        // 注册框架预定义的默认类型处理器（如日期、数值、字符串等类型的转换）
        typeHandlerRegistry.registerDefaultTypeHandlers();
    }

    /**
     * 添加SQL映射语句到配置中
     * 用于将解析后的SQL映射信息（MappedStatement）存入缓存集合
     * 若存在重复的映射ID，会通过StrictMap抛出异常
     *
     * @param ms 封装了SQL映射信息的MappedStatement对象
     */
    public void addMappedStatement(MappedStatement ms) {
        mappedStatements.put(ms.getId(), ms);
    }

    /**
     * 根据映射ID获取对应的SQL映射语句
     * 是执行SQL时的核心查询方法，通过ID定位到具体的SQL配置
     *
     * @param id 映射语句的唯一标识（namespace+statementId）
     * @return 对应的MappedStatement对象，若不存在则返回null
     */
    public MappedStatement getMappedStatement(String id) {
        return mappedStatements.get(id);
    }

    /**
     * 判断指定ID的SQL映射语句是否已存在
     * 用于避免重复添加映射语句，或校验配置是否正确
     *
     * @param id 映射语句的唯一标识
     * @return 存在返回true，否则返回false
     */
    public boolean hasMappedStatement(String id) {
        return mappedStatements.containsKey(id);
    }

    /**
     * 添加缓存实例到配置中
     * 用于注册自定义缓存或框架默认缓存，支持按ID区分不同的缓存区域
     *
     * @param cache 缓存实例，需实现Cache接口并提供唯一ID
     */
    public void addCache(Cache cache) {
        caches.put(cache.getId(), cache);
    }

    /**
     * 根据缓存ID获取对应的缓存实例
     * 用于查询操作时获取缓存，优先从缓存获取结果以提升性能
     *
     * @param id 缓存的唯一标识（通常为Mapper接口全类名）
     * @return 对应的缓存实例，若不存在则返回null
     */
    public Cache getCache(String id) {
        return caches.get(id);
    }

    // ======================== Getter 和 Setter 方法 ========================
    /**
     * 获取环境配置
     *
     * @return 环境配置对象Environment
     */
    public Environment getEnvironment() {
        return environment;
    }

    /**
     * 设置环境配置，并同步获取环境中的数据源
     * 绑定环境时自动关联数据源，简化配置流程
     *
     * @param environment 环境配置对象
     */
    public void setEnvironment(Environment environment) {
        this.environment = environment;
        this.dataSource = environment.getDataSource();
    }

    /**
     * 获取数据源
     *
     * @return 数据源实例DataSource
     */
    public DataSource getDataSource() {
        return dataSource;
    }

    /**
     * 手动设置数据源
     * 支持绕过环境配置，直接指定数据源（适用于特殊场景）
     *
     * @param dataSource 数据源实例
     */
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    /**
     * 获取类型别名注册中心
     *
     * @return 类型别名注册中心实例
     */
    public TypeAliasRegistry getTypeAliasRegistry() {
        return typeAliasRegistry;
    }

    /**
     * 获取类型处理器注册中心
     *
     * @return 类型处理器注册中心实例
     */
    public TypeHandlerRegistry getTypeHandlerRegistry() {
        return typeHandlerRegistry;
    }

    /**
     * 判断全局缓存是否开启
     *
     * @return 开启返回true，否则返回false
     */
    public boolean isCacheEnabled() {
        return cacheEnabled;
    }

    /**
     * 设置全局缓存开关
     *
     * @param cacheEnabled 缓存开关状态
     */
    public void setCacheEnabled(boolean cacheEnabled) {
        this.cacheEnabled = cacheEnabled;
    }

    /**
     * 判断是否开启主键自动生成
     *
     * @return 开启返回true，否则返回false
     */
    public boolean isUseGeneratedKeys() {
        return useGeneratedKeys;
    }

    /**
     * 设置主键自动生成开关
     *
     * @param useGeneratedKeys 主键生成开关状态
     */
    public void setUseGeneratedKeys(boolean useGeneratedKeys) {
        this.useGeneratedKeys = useGeneratedKeys;
    }

    /**
     * 获取默认SQL执行超时时间
     *
     * @return 超时时间（秒），未配置则返回null
     */
    public Integer getDefaultStatementTimeout() {
        return defaultStatementTimeout;
    }

    /**
     * 设置默认SQL执行超时时间
     *
     * @param defaultStatementTimeout 超时时间（秒）
     */
    public void setDefaultStatementTimeout(Integer defaultStatementTimeout) {
        this.defaultStatementTimeout = defaultStatementTimeout;
    }

    /**
     * 获取默认结果集抓取大小
     *
     * @return 抓取大小，未配置则返回null
     */
    public Integer getDefaultFetchSize() {
        return defaultFetchSize;
    }

    /**
     * 设置默认结果集抓取大小
     *
     * @param defaultFetchSize 抓取大小
     */
    public void setDefaultFetchSize(Integer defaultFetchSize) {
        this.defaultFetchSize = defaultFetchSize;
    }

    /**
     * 获取日志实现类
     *
     * @return 日志实现类实例
     */
    public LogImpl getLogImpl() {
        return logImpl;
    }

    /**
     * 设置日志实现类
     *
     * @param logImpl 日志实现类实例
     */
    public void setLogImpl(LogImpl logImpl) {
        this.logImpl = logImpl;
    }

    /**
     * 获取事务工厂
     *
     * @return 事务工厂实例
     */
    public TransactionFactory getTransactionFactory() {
        return transactionFactory;
    }

    /**
     * 设置事务工厂
     * 支持替换为自定义事务工厂，适配不同的事务管理机制
     *
     * @param transactionFactory 自定义事务工厂实例
     */
    public void setTransactionFactory(TransactionFactory transactionFactory) {
        this.transactionFactory = transactionFactory;
    }

    /**
     * 严格模式的Map实现
     * 继承HashMap，重写put方法，防止相同key重复插入
     * 专门用于存储MappedStatement，保证SQL映射ID的全局唯一性
     *
     * @param <V> 存储的值类型（此处特指MappedStatement）
     */
    protected static class StrictMap<V> extends HashMap<String, V> {
        // Map的功能描述，用于异常提示（如"SQL映射语句集合"）
        private final String name;

        /**
         * 构造方法，指定Map的功能描述
         *
         * @param name Map的功能名称，用于异常信息中区分不同的StrictMap实例
         */
        public StrictMap(String name) {
            this.name = name;
        }

        /**
         * 重写put方法，添加重复key校验
         * 当插入已存在的key时，抛出IllegalArgumentException异常，避免配置冲突
         *
         * @param key 映射键（此处为MappedStatement的ID）
         * @param value 映射值（此处为MappedStatement实例）
         * @return 插入成功的value
         * @throws IllegalArgumentException 当key已存在时抛出
         */
        @Override
        public V put(String key, V value) {
            if (containsKey(key)) {
                // 抛出重复key异常，明确提示冲突的配置项
                throw new IllegalArgumentException(name + " already contains value for " + key);
            }
            return super.put(key, value);
        }
    }
}